package ca.concordia.soen6461.wichf.service;

import java.util.Date;
import java.util.List;

import ca.concordia.soen6461.wichf.dao.PatientCartDao;
import ca.concordia.soen6461.wichf.dao.ScheduledEventDao;
import ca.concordia.soen6461.wichf.entity.PatientCart;
import ca.concordia.soen6461.wichf.entity.ScheduledEvent;
import ca.concordia.soen6461.wichf.entity.ScheduledEvent.Type;
import ca.concordia.soen6461.wichf.entity.User;

public class SchedulingServiceImpl implements SchedulingService {

	private static final int MINUTE = 60 * 1000;

	private ScheduledEventDao scheduledEventDao;
	private PatientCartDao patientCartDao;
	
	public SchedulingServiceImpl(ScheduledEventDao scheduledEventDao, PatientCartDao patientCartDao) {
		this.scheduledEventDao = scheduledEventDao;
		this.patientCartDao = patientCartDao;
	}
	
	private PatientCart getCart(User user) {
		PatientCart cart = patientCartDao.get(user);
		if (cart == null) {
			cart = new PatientCart();
			cart.setPatient(user);
			patientCartDao.save(cart);
		}
		return cart;
	}

	@Override
	public ScheduledEvent makeAppointment(User user, Date from, int duration, Type type) {
		ScheduledEvent e = new ScheduledEvent();
		e.setUser(user);
		e.setFrom(from);
		e.setTo(new Date(from.getTime() + duration * MINUTE));
		e.setType(type);
		scheduledEventDao.save(e);
		PatientCart cart = getCart(user);
		if (!cart.getAppointments().contains(e)) {
			cart.getAppointments().add(e);
		}
		return e;
	}

	@Override
	public ScheduledEvent cancelAppointment(User user, Date from) {
		ScheduledEvent scheduledEvent = scheduledEventDao.delete(user, from);
		return scheduledEvent;
	}

	@Override
	public ScheduledEvent changeAvailability(User user, Date from, Date to, boolean available) {
		ScheduledEvent e = null;
		List<ScheduledEvent> events = scheduledEventDao.find(from, to, user, ScheduledEvent.Type.DoctorAvailability);
		for (ScheduledEvent event : events) {
			scheduledEventDao.delete(user, event.getFrom());
		}
		Date first = events.size() > 0 ? events.get(0).getFrom() : from;
		Date last = events.size() > 0 ? events.get(events.size() - 1).getTo() : to;
		if (available) {
			e = new ScheduledEvent();
			e.setUser(user);
			e.setType(ScheduledEvent.Type.DoctorAvailability);
			e.setFrom(first.compareTo(from) < 0 ? first : from);
			e.setTo(last.compareTo(to) > 0 ? last : to);
			scheduledEventDao.save(e);
		} else {
			if (first.compareTo(from) < 0) {
				ScheduledEvent firstEvent = events.get(0);
				ScheduledEvent event = new ScheduledEvent();
				event.setUser(user);
				event.setType(ScheduledEvent.Type.DoctorAvailability);
				event.setFrom(firstEvent.getFrom());
				event.setTo(from);
				scheduledEventDao.save(event);
			}
			if (last.compareTo(to) > 0) {
				ScheduledEvent lastEvent = events.get(events.size() - 1);
				ScheduledEvent event = new ScheduledEvent();
				event.setUser(user);
				event.setType(ScheduledEvent.Type.DoctorAvailability);
				event.setFrom(to);
				event.setTo(lastEvent.getTo());
				scheduledEventDao.save(event);
			}
		}
		return e;
	}

	@Override
	public ClinicStatus clinicStatus(User user, Date time) {
		List<ScheduledEvent> events = scheduledEventDao.find(time);
		int availableDoctors = 0;
		int appointmentsByOthers = 0;
		ScheduledEvent userAppointment = null;
		for (ScheduledEvent event : events) {
			if (event.getType() == ScheduledEvent.Type.DoctorAvailability) {
				availableDoctors++;
			}
			if (event.getUser().getId().equals(user.getId())) {
				userAppointment = event;
			} else if (event.isConfirmed()) {
				appointmentsByOthers++;
			}
		}
		int maxCapacity = Math.min(availableDoctors, SchedulingService.ROOM_COUNT);
		List<ScheduledEvent> userEvents = scheduledEventDao.find(user);
		boolean exhaustedCheckups = false;
		for (ScheduledEvent event : userEvents) {
			exhaustedCheckups = event.getType() == ScheduledEvent.Type.PatientCheckup;
			if (exhaustedCheckups) break;
		}
		return new ClinicStatus(
			userAppointment, 
			maxCapacity > appointmentsByOthers && userAppointment == null, 
			exhaustedCheckups
		);
	}

}
